#include "StdAfx.h"
#include "WebPageTab.h"
#include <cassert>
#include <algorithm>

const int TAB_WIDTH = 80;
const int TAB_TITLE_WIDTH = 20;
CTabHost* CTabHost::sm_pTabHost = NULL;

CTabHost* CTabHost::GetTabHostInstance(HWND hWndMain)
{
	if ( sm_pTabHost == NULL )
	{
		sm_pTabHost = new CTabHost();

		if ( hWndMain != NULL )
			sm_pTabHost->Init(hWndMain);
	}

	return sm_pTabHost;
}

void CTabHost::ReleaseTabHost()
{
	if ( sm_pTabHost != NULL )
	{
		delete sm_pTabHost;
		sm_pTabHost = NULL;
	}
}

LRESULT __stdcall CTabHost::HostWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	if ( WM_CREATE == msg )
	{
		CREATESTRUCT *cs = (CREATESTRUCT *)(void*)(lParam);
		::SetWindowLong(hWnd, GWL_USERDATA, (long)cs->lpCreateParams);
		return 0;
	}

	LONG lThis = ::GetWindowLong(hWnd, GWL_USERDATA);
	CTabHost *pThis = reinterpret_cast<CTabHost *>(lThis);

	if ( pThis == NULL )
		return ::DefWindowProc(hWnd, msg, wParam, lParam);
	else
		return pThis->WindowProc(hWnd, msg, wParam, lParam);
}


CTabHost::CTabHost()
{
	m_bAutoHide = false;
	m_bVisible = true;
	m_activeTabId = 0;
	m_mouseDownTabId = 0;
	m_tabsPosOffset = 0;
	m_szMemDc.cx = 0;
	m_szMemDc.cy = 0;
	m_hMemDc = NULL;
	m_hWndParentMain = NULL;
	m_hWndTabsHost = NULL;

	m_pTabWatcher = NULL;
}

CTabHost::~CTabHost()
{
}

void CTabHost::Init(HWND hWndMain)
{
	if ( m_hWndParentMain != NULL )
		return;

	assert(m_hMemDc == NULL);
	assert(m_hWndTabsHost == NULL);
	m_hWndParentMain = hWndMain;

	WNDCLASSEX wcex = {0};
	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= HostWndProc;	
	wcex.hInstance		= ::GetModuleHandle(0);
	wcex.lpszClassName	= _T("TabsHostWnd");
	RegisterClassEx(&wcex);

	CRect rctMainClient;
	::GetClientRect(hWndMain, &rctMainClient);

	m_hWndTabsHost = ::CreateWindowW(wcex.lpszClassName, NULL, WS_CHILD|WS_VISIBLE, 
		0, 0, rctMainClient.right, 20, 
		hWndMain, NULL, wcex.hInstance, this);	

	CSize szHost(rctMainClient.right, 20);
	ResizeTabHost(szHost, NULL);
}

bool CTabHost::ActivateTab(int id)
{
	TAB_COLLECTION &tabs = m_tabs;
	if ( tabs.find(id) != tabs.end() )
	{
		tabs[id].m_active = true;

		tabs[m_activeTabId].m_active = false;
		m_activeTabId = id;
		DrawAllTabs(true, true);
		return true;
	}

	return false;
}

bool CTabHost::PushNewTab(int id)
{
	TAB_COLLECTION &tabs = m_tabs;
	if ( tabs.find(id) == tabs.end() )
	{
		tabs[id].m_id = id;
		tabs[id].m_active = false;
		tabs[id].m_lock = false;
		tabs[id].m_visible = true;
		tabs[id].m_data = 0;
		tabs[id].m_pVoid = NULL;

		m_order.push_back(id);

		if ( m_order.size() == 1 )
			m_activeTabId = id;

		ArrangeTabPos();	
		DrawAllTabs(true, true);
	}

	return true;
}

bool CTabHost::RemoveTab(int id)
{
	TAB_COLLECTION &tabs = m_tabs;
	TAB_ORDER &order = m_order;
	TAB_ORDER::iterator it = std::find(order.begin(), order.end(), id);
	if ( it != order.end() )
	{
		tabs.erase(id);
		order.erase(it);
		ArrangeTabPos();
		DrawAllTabs(true, true);
		return true;
	}

	return true;
}

void CTabHost::SetTabActionWacther(CTabActionWatcher *watcher)
{	
	m_pTabWatcher = watcher;
}

CRect CTabHost::GetTabHostPosSize()
{
	CRect rctWnd;	
	rctWnd.SetRectEmpty();
	
	if ( m_bVisible )
	{
		GetWindowRect(m_hWndTabsHost, &rctWnd);
	}
	return rctWnd;
}

void CTabHost::ResizeTabHost(CSize szNew, CPoint *pLeftTop)
{
	if ( m_hMemDc != NULL )
		DeleteDC(m_hMemDc);	

	m_szMemDc = szNew;
	CPoint pt(0, 0);
	if ( pLeftTop != NULL )
		pt = *pLeftTop;

	HDC hdc = ::GetDC(m_hWndParentMain);
	m_hMemDc = ::CreateCompatibleDC(hdc);
	HBITMAP hBmp = ::CreateCompatibleBitmap(hdc, szNew.cx, szNew.cy);
	::SelectObject(m_hMemDc, hBmp);
	::ReleaseDC(m_hWndParentMain, hdc);
	::DeleteObject(hBmp);

	ArrangeTabPos();	
	DrawAllTabs(true, false);
	::SetWindowPos(m_hWndTabsHost, NULL, pt.x, pt.y, szNew.cx, szNew.cy, SWP_NOACTIVATE);	
}

bool CTabHost::ShowTab(int id, bool show)
{
	TAB_COLLECTION &tabs = m_tabs;
	if ( tabs.find(id) != tabs.end() )
	{
		tabs[id].m_visible = show;
		ArrangeTabPos();
		DrawAllTabs(true, true);
	}

	return false;
}

bool CTabHost::ShowHost(bool show)
{
	m_bVisible = show;
	::ShowWindow(m_hWndTabsHost, show ? SW_SHOWNA : SW_HIDE);
	return true;
}

void CTabHost::DrawAllTabs(bool bDraw, bool bUpdate)
{
	TAB_COLLECTION &tabs = m_tabs;
	TAB_ORDER &order = m_order;
	TAB_ORDER::iterator it;
	HDC hdc = GetDC(m_hWndTabsHost);

	CRect rctClient;
	GetClientRect(m_hWndTabsHost, &rctClient);

	if ( bDraw )
	{
		::SetDCBrushColor(m_hMemDc, RGB(200, 250, 240));
		::SetDCPenColor(m_hMemDc, RGB(80, 255, 0));
		SelectObject(m_hMemDc, GetStockObject(DC_PEN));
		SelectObject(m_hMemDc, GetStockObject(DC_BRUSH));
		Rectangle(m_hMemDc, 0, 0, m_szMemDc.cx, m_szMemDc.cy);

		SetDCBrushColor(m_hMemDc, RGB(0, 200, 0));
		SetDCPenColor(m_hMemDc, RGB(0, 20, 200));
		SelectObject(m_hMemDc, GetStockObject(DC_PEN));
		SelectObject(m_hMemDc, GetStockObject(DC_BRUSH));	
		SetBkMode(m_hMemDc, TRANSPARENT);

		TAB_COLLECTION &tabs = m_tabs;
		TAB_ORDER &order = m_order;
		TAB_ORDER::iterator it;
		for ( it = order.begin(); it != order.end(); it++ )
		{
			int id = *it;
			if ( tabs[id].m_visible )
			{
				TCHAR pszId[10];
				_stprintf_s(pszId, 10, _T("%d"), id);
				CRect rct = tabs[id].m_rctTabInHost;
				
				CSize szExtent;
				GetTextExtentPoint(m_hMemDc, pszId, (int)_tcslen(pszId), &szExtent);
			
				SetTextColor(m_hMemDc, m_activeTabId == id ? 0xFF00FF : 0xFF0000);
				DrawText(m_hMemDc, pszId, -1, &rct, DT_LEFT|DT_SINGLELINE|DT_END_ELLIPSIS);
				
				MoveToEx(m_hMemDc, rct.left, rct.top, NULL);
				LineTo(m_hMemDc, rct.left, rct.bottom);

				rct.left += szExtent.cx;
				SetTextColor(m_hMemDc, 0x000000);

				TCHAR pszTitle[100];
				::GetWindowText(m_hWndTabsHost, pszTitle, 100);
				DrawText(m_hMemDc, pszTitle, -1, &rct, DT_LEFT|DT_SINGLELINE|DT_END_ELLIPSIS);
			}
		}
	}

	if ( bUpdate )
	{
		BitBlt(hdc, 0, 0, rctClient.right, rctClient.bottom, m_hMemDc, 0, 0, SRCCOPY);
	}

	ReleaseDC(m_hWndTabsHost, hdc);
}

void CTabHost::ArrangeTabPos()
{
	TAB_COLLECTION &tabs = m_tabs;
	TAB_ORDER &order = m_order;
	TAB_ORDER::iterator it;
	
	CRect rctClient;
	CRect rctTab;
	::GetClientRect(m_hWndTabsHost, &rctClient);
	rctTab = CRect(TAB_TITLE_WIDTH, 0, TAB_WIDTH, rctClient.Height());

	for ( it = order.begin(); it != order.end(); ++it )
	{
		int id = *it;
		assert(tabs.find(id) != tabs.end());
		if ( tabs[id].m_visible )
		{
			tabs[id].m_rctTabInHost = rctTab;
			rctTab.OffsetRect(TAB_WIDTH, 0);
		}
		else
		{
			tabs[id].m_rctTabInHost.SetRectEmpty();
		}
	}
}

LRESULT CTabHost::WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	if ( msg == WM_LBUTTONUP )
	{
		if ( m_pTabWatcher == NULL )
			return 0;

		CPoint pt(LOWORD(lParam), HIWORD(lParam));
		TAB_COLLECTION &tabs = m_tabs;
		TAB_COLLECTION::iterator it;

		for ( it = tabs.begin(); it != tabs.end(); it++ )
		{
			int id = it->first;
			STab tab = it->second;
			if ( !tab.m_visible || id != m_mouseDownTabId )
				continue;

			if ( tab.m_rctTabInHost.PtInRect(pt) )
				m_pTabWatcher->OnTabClick(id);
		}

		m_mouseDownTabId = 0;
	}
	else if ( msg == WM_LBUTTONDOWN )
	{		
		if ( m_pTabWatcher == NULL )
			return 0;

		CPoint pt(LOWORD(lParam), HIWORD(lParam));
		TAB_COLLECTION &tabs = m_tabs;
		TAB_COLLECTION::iterator it;

		for ( it = tabs.begin(); it != tabs.end(); it++ )
		{
			int id = it->first;
			STab tab = it->second;
			if ( !tab.m_visible )
				continue;

			if ( tab.m_rctTabInHost.PtInRect(pt) )
			{
				m_mouseDownTabId = id;
				break;
			}
		}
	}
	else if ( msg == WM_LBUTTONDBLCLK )
	{
		if ( m_pTabWatcher == NULL )
			return 0;

		CPoint pt(LOWORD(lParam), HIWORD(lParam));
		TAB_COLLECTION &tabs = m_tabs;
		TAB_COLLECTION::iterator it;

		for ( it = tabs.begin(); it != tabs.end(); it++ )
		{
			int id = it->first;
			STab tab = it->second;
			if ( !tab.m_visible )
				continue;

			if ( tab.m_rctTabInHost.PtInRect(pt) )
			{
				m_pTabWatcher->OnTabDblClick(id);
				break;
			}
		}
	}
	else if ( msg == WM_PAINT )
	{
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(hWnd, &ps);

		CRect rctClient;
		GetClientRect(m_hWndTabsHost, &rctClient);
		BitBlt(hdc, 0, 0, rctClient.right, rctClient.bottom, m_hMemDc, 0, 0, SRCCOPY);		
		EndPaint(hWnd, &ps);
		return 0;
	}
	else if ( msg == WM_ERASEBKGND )
	{
		CRect rctClient;
		GetClientRect(m_hWndTabsHost, &rctClient);
		BitBlt((HDC)wParam, 0, 0, rctClient.right, rctClient.bottom, m_hMemDc, 0, 0, SRCCOPY);
		return 1;
	}
	else if ( msg == WM_WINDOWPOSCHANGED )
	{
		BOOL ret = ::SetWindowPos(m_hWndTabsHost, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE);
		return 0;
	}

	return ::DefWindowProc(hWnd, msg, wParam, lParam);
}